iT邦幫忙

2026 iThome 鐵人賽

DAY 30
0
Claude AI

資深工程師的 Claude Code 工作筆記系列 第 30 篇

Day 30:Claude Code 自己出問題時怎麼查,沒載入、沒匹配,還是被蓋掉

  • 分享至 

  • xImage
  •  

hooks、subagent、skills、MCP、plugin、mod、權限規則,Day 17 到 Day 29 一路往 Claude Code 身上加東西。加得越多,遇到的問題越不像程式的 bug,因為它通常不報錯,只是沒反應:hook 沒跑、skill 沒被叫到、MCP 的工具沒出現、改了設定卻像沒改。

這種「沒反應」幾乎都可以歸到三類:

  • 沒載入:東西放錯位置、檔名不對、格式壞了,Claude Code 根本沒讀進來。
  • 載入了但沒匹配:讀進來了,但條件對不上,例如 hook 的 matcher 打錯字、skill 的描述跟你的說法對不上。
  • 被別層蓋掉:設定寫對了,但另一個優先權更高的檔案設了同一個 key。

先判斷是哪一類,再去看對應的入口。這篇整理每一類該看哪裡、輸出長什麼樣,以及幾個不會報錯的坑。

症狀先分三類再看入口

兩個 doctor 不是同一個東西

最直覺的入口是 doctor,但它有兩個版本:

  • shell 的 claude doctor:不開 session,唯讀,印出安裝狀態、版本、更新、settings 檔的驗證錯誤。claude 本身起不來的時候用這個。
  • session 裡的 /doctor:2.1.205 起改成內建 skill,別名 /checkup。它會檢查安裝、沒在用的 skills 與 MCP、太慢的 hook、重複或過長的 CLAUDE.md、權限設定,提出修正,問過你才改。/doctor prompt-audit 另外檢查 CLAUDE.md 與 skills 裡為舊模型寫的提示。

claude doctor 只看安裝與設定檔能不能解析,不看 hook 有沒有匹配、MCP 連不連得上。它的最後一行寫 No installation issues found 時,上面幾行仍可能有 Last update attempt: failed,要往上讀,不要只看結尾。

Running: native (2.1.299)
Auto-updates: enabled
Last update attempt: failed (install_failed)
No installation issues found.
For a full setup checkup that can also fix issues, run /doctor in a Claude Code session.

在 -p 模式下 settings 檔壞掉不會跳對話框,被丟掉的設定只會在 claude doctor 看得到。無頭跑的腳本行為怪怪的,先在同一台機器跑一次 claude doctor。

先確認有沒有載入:/context

官方 debug 設定的頁面把 /context 放在第一步:先確認你的 CLAUDE.md、rules、skill 描述到底有沒有在 context 裡。它依類別列出 system prompt、工具、MCP 工具、自訂 subagent(含來源)、memory files、skills、對話訊息各佔多少。

幾個要知道的規則:

  • 子目錄的 CLAUDE.md 是用到才載入,Claude 讀寫那個子目錄的檔案時才會進來,所以剛開 session 時不會出現在 Memory files 裡。這不是壞掉。
  • skills 的描述清單有預算,預設是 context window 的 1%。超過時從最少用的 skill 開始丟描述,/context 的 Skills 列顯示的是套用預算之後的大小。skill 裝很多卻有幾個從來沒被叫到,先看這裡。
  • 內建的 Explore 與 Plan subagent 啟動時會略過 CLAUDE.md。寫在 CLAUDE.md 的規則它們看不到,要寫進派工的 prompt 裡。

/context 看不到的東西,Claude 就看不到。這一步排除掉「沒載入」,後面才值得往下查。

另外幾個專門的畫面:/memory 列出會載入的 CLAUDE.md 與記憶檔位置;/hooks 是唯讀的 hook 瀏覽器,每個 hook 標出來自 user、project、local、plugin 哪一層;/mcp 看每個 server 的狀態;/permissions 看目前生效的 allow 與 deny;/skills 會在 Claude 不會自己觸發的 skill 上標 user-only。

debug log:不會印到終端機

很多人開了 --debug 之後盯著終端機,什麼都沒看到。官方文件寫得很直接:--debug 不會印到終端機,log 寫在 ~/.claude/debug/<session-id>.txt。

# 寫到固定位置,另開一個終端機看
claude --debug-file /tmp/cc-debug.log
tail -f /tmp/cc-debug.log

# 只看特定類別,過濾條件一定要用等號
claude --debug=hooks
claude --debug='mcp,startup'

--debug 後面接過濾類別時要用 = 的寫法。中間用空白隔開,debug 會開,但過濾不會生效,整份 log 全進來。! 開頭代表排除,例如 --debug='!1p'。

session 已經開了才發現要看 log,不用重開,在 session 裡打 /debug,從那一刻開始記錄,它也會幫你讀 log。想看更細的內容(例如 matcher 的比對過程),設 CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose。

hook 沒反應:最常見的是靜默失效

hook 的問題最難查,因為寫錯通常不會報錯。matcher 打錯字的 hook 不會出現在 debug log 的任何一行,Claude Code 不警告也不報錯,就是不跑。

所以查法是反過來:看預期該出現的那一行有沒有出現。2.1.296 起,每個掛在工具呼叫、prompt、SessionStart、Stop 上的 command hook 結束時,debug log 會留一行指令、結果與耗時:

Hooks: PreToolUse:Bash [./hooks/check-command.sh] finished with status 0 (6ms)

用一個示意設定說明。下面兩個 hook 只差一個字:

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "bash", "hooks": [{ "type": "command", "command": "./hooks/check-command.sh" }] },
      { "matcher": "Bash", "hooks": [{ "type": "command", "command": "./hooks/check-command.sh" }] }
    ]
  }
}

matcher 區分大小寫,工具名是大寫開頭的 Bash、Edit、Write、Read。第一個永遠不會匹配,debug log 只會看到第二個留下的 PreToolUse:Bash 那一行。log 裡找不到你的 hook,就是沒匹配或沒載入。

官方 debug 設定頁整理的「hook 從來沒觸發」原因,加上幾個常見的:

症狀 原因 怎麼改
完全不觸發 matcher 寫成 JSON 陣列 改成單一字串,用 | 分隔,例如 "Edit|Write"
完全不觸發 2.1.191 以前用 , 分隔 改用 |,或升級
完全不觸發 matcher 用小寫 工具名大寫開頭
完全不觸發 hooks 寫在獨立檔案 寫進 settings.json 的 hooks key,只有 plugin 才讀 hooks/hooks.json
全域設定沒作用 寫進了 ~/.claude.json 那是程式狀態檔,hooks、permissions、env 要放 ~/.claude/settings.json
互動模式不跑 還沒接受 workspace trust 對話框 接受後才會跑,連 user 層的 hook 也一樣

matcher 寫成陣列算是少數會報錯的情況:互動 session 啟動時與 claude doctor 都會把它列為無效設定。它還有一個副作用:在 PreToolUse 與 PermissionRequest 底下,同一個檔案裡的其他 hook 也會一起不載入。

另一種是「跑了但沒擋住」。Day 17 講過退出碼:只有 2 會擋,其他非零只是非阻擋錯誤,動作照樣進行,逐字稿會出現 <hook name> hook error。腳本忘了 chmod +x、路徑寫相對路徑從別的目錄找不到,都會落到這一類,等於沒擋就放行。拿 hook 當守門時,在設定裡加 onFailure: "block",讓 hook 本身壞掉時也擋下來。

還有兩個 log 裡可以搜的字串:

  • Hook output does not start with {:hook 想輸出 JSON,但 shell profile 先 echo 了東西,JSON 被當成純文字。
  • Hook JSON output had unrecognized keys:欄位放錯層,例如 permissionDecision 要放在 hookSpecificOutput 裡面。

改 settings.json 之後不用重開 session,Claude Code 會監看設定檔,短暫延遲後套用。/hooks 還顯示舊定義就再開一次,官方另一頁說等幾秒還沒出現可以重開 session。

設定沒生效:被哪一層蓋掉

settings 有五層,同一個 key 出現在多個地方時,取最高那層的值:

  1. Managed settings(組織派送的政策檔)
  2. 命令列的 --settings
  3. .claude/settings.local.json
  4. .claude/settings.json
  5. ~/.claude/settings.json

五層設定,同一個 key 取最高那層

有三個例外要記得:

  • 陣列會合併。permissions.allow 這類清單在多個檔案都有時,Claude Code 會把它們合起來,不是挑一份。所以在 local 把 allow 清空了卻還是放行,原因是 project 那份還在。權限規則另有組合規則,例如 local 的 allow 壓不過 project 或 managed 的 ask。
  • 環境變數不在這五層裡,是逐個 key 決定。ANTHROPIC_MODEL 會蓋過任何檔案裡的 model,--model 旗標也一樣。設定檔裡的 env 區塊則是普通 key,照五層算。
  • 少數 key 只在啟動時讀一次,例如 model,session 中改檔不會換,要用 /model。

最麻煩的是:官方沒有提供「這個值來自哪一層」的查詢。/status 的 Setting sources 只列出這次讀了哪些檔案,官方原文寫明它不會顯示每個 key 是哪個檔給的。/config 的 Config 分頁也不是 settings.json 的內容。

可行的做法是用 --setting-sources 一層一層隔開,看結果什麼時候變。以 model 為例,/context 的輸出會帶目前的模型,可以這樣二分:

for s in user project local project,local user,project,local; do
  printf '%-22s ' "$s"
  claude -p "/context" --setting-sources "$s" | grep -m1 "Model"
done

只留 project 是 haiku、加上 local 就變 opus,答案就在 local。--setting-sources 管的是 user、project、local 三層,managed 不受影響。其他 key 沒有這麼方便的顯示管道,就換成看行為,例如 hook 有沒有出現在 debug log。

還有兩個「寫錯檔」的經典:

  • ~/.claude.json 與 ~/.claude/settings.json 是兩個不同的檔。前者是 Claude Code 自己寫的狀態,放登入、MCP server、各專案的信任狀態;權限、hook、env 要放後者。
  • 自己建的 .claude/settings.local.json 要自己加進 .gitignore,Claude Code 建的才會自動排除。

skill 沒被叫到

先分清楚是「沒載入」還是「沒匹配」:

  • /skills 裡沒有:多半是位置錯。要放成 .claude/skills/name/SKILL.md,直接放 .claude/skills/name.md 不會出現。另外 --- 必須在檔案第一行。
  • /skills 裡有,但 Claude 從不自己用:看有沒有 user-only 標籤,那是 disable-model-invocation: true,設計上就不會自動觸發。沒有的話,就是 description 跟你平常的說法對不上,把你會講的關鍵字寫進 description。

frontmatter 的 YAML 壞掉時,官方的描述是:skill 本體照樣載入,但 metadata 是空的。所以 /skill-name 還能用,Claude 卻永遠匹配不到它,看起來像「只有手動叫得到」。跑 --debug 可以看到 parse error。

也可以不開 session 先檢查:

claude plugin validate .claude/skills

2.1.233 起它能檢查一般專案的 .claude/skills 目錄。缺 description 只會給警告,結果仍是 Validation passed with warnings,所以要讀警告,不要只看 passed。

MCP 的工具沒出現

先用 shell 看狀態,不用開 session:

claude mcp list
claude mcp get <name>

專案 .mcp.json 裡的 server 第一次要核准,沒核准之前狀態是 ⏸ Pending approval,根本不會啟動。指令路徑寫錯的 server 也只會顯示 Pending approval,看不出它其實起不來。要在互動 session 的 /mcp 核准之後,才會看到真正的錯誤。

真正的失敗原因在 debug log,server 的 stderr 也記在裡面:

claude --debug=mcp
MCP server "local-db": Starting connection with timeout of 30000ms
MCP server "local-db": Connection failed after 72ms (ENOENT)

常見的位置錯誤,官方都列過:

  • 專案 MCP 設定放在 repo 根目錄的 .mcp.json,不是 .claude/ 裡面;server 放在 mcpServers key 下,不是 VS Code 用的 servers。
  • settings.json 不讀 mcpServers。使用者層級的 server 用 claude mcp add --scope user,寫進 ~/.claude.json。
  • command 與 args 的相對路徑是相對於啟動 Claude Code 的目錄,不是 .mcp.json 所在的地方,用絕對路徑。
  • .mcp.json 只在 session 開始時讀,改了要重開。

啟動逾時預設 30 秒,慢的 server 用 MCP_TIMEOUT=60000 claude 放寬,單位是毫秒。連得上但工具數是零,先在 /mcp 選 Reconnect,再不行就看 --debug=mcp 的 stderr。

拿一個乾淨的環境對照

查不出是哪個設定搞的鬼時,先確認是不是你的客製化造成的:

claude --safe-mode

它會關掉 CLAUDE.md、skills、plugins、hooks、MCP、自訂指令與 agent、output styles、keybindings 等所有客製化,登入、模型、內建工具與權限照常,managed 的政策仍然套用。safe mode 下正常,問題就在你的設定裡,接著用 --setting-sources 或逐一關掉 plugin 縮小範圍。

要更徹底,可以換一個全新的設定目錄:

CLAUDE_CONFIG_DIR=~/claude-clean/config claude --setting-sources user

CLAUDE_CONFIG_DIR 會把整個 ~/.claude 換到別處,設定、session 歷史與 plugins 都在那裡,不會碰到原本的。

逐字稿在哪、能不能拿來分析

每個 session 的逐字稿存在 ~/.claude/projects/<project>/<session-id>.jsonl,<project> 是工作目錄路徑把非英數字元換成 -。subagent 的逐字稿在同一個 session 目錄下的 subagents/。

jsonl 裡記得很完整,訊息、工具呼叫、hook 的執行紀錄與錯誤都在裡面。但官方明講這是內部格式,每個版本都可能變,直接解析的腳本隨時會壞。要拿 session 資料做事,用 /export,或在 hook 與 status line 的輸入 JSON 裡讀 transcript_path。

兩件要注意的事:

  • 逐字稿沒有加密。工具讀過的 .env、指令印出來的金鑰,都原樣留在 jsonl 裡。
  • 預設保留 30 天,由 cleanupPeriodDays 控制。

真的是 bug 才回報

確定不是設定問題,再回報:

  • /bug:回報問題,2.1.212 起跟 /feedback 分開。用第三方 provider 時,它會把報告寫到 ~/.claude/feedback-bundles/,讓你自己轉交。
  • /heapdump:記憶體或 CPU 異常時用,會在桌面寫出 heap snapshot 與一份 -diagnostics.json。官方提醒只附 -diagnostics.json,.heapsnapshot 裡有完整對話與憑證,不要分享。

從症狀到入口的對照

今天的重點

  • 「沒反應」先分三類:沒載入、載入了沒匹配、被別層蓋掉。/context 看不到的東西,Claude 就看不到,先用它排除第一類。
  • claude doctor 在 shell 跑,只看安裝與設定檔;/doctor 在 session 裡,會提修正。claude doctor 要讀完整份輸出,不要只看最後一行。
  • --debug 寫進 ~/.claude/debug/,不印在終端機。過濾類別要用 --debug=hooks 的等號寫法,session 中途用 /debug。
  • hook 寫錯通常是靜默失效,查法是看 debug log 有沒有預期的那一行。只有退出碼 2 會擋,守門 hook 加 onFailure: "block"。
  • settings 五層取最高、陣列合併、環境變數逐個 key 決定。官方沒有「值來自哪層」的查詢,用 --setting-sources 二分。
  • skill 看位置、frontmatter 與 description;MCP 看 claude mcp list、核准狀態與 --debug=mcp。查不出來就 claude --safe-mode 對照。

上一篇
Day 29:Mods,改 Claude Code 自己的那一層,去哪裡找、怎麼挑、怎麼寫
系列文
資深工程師的 Claude Code 工作筆記 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言